#include "src/scene/areaLight.h"

#include <iostream>

#include "src/scene/material.h"

AreaLight::AreaLight() : Light() {
	position = Vector3D(-0.5, -0.5, 4.0);
	a = Vector3D(1, 0, 0);
	b = Vector3D(0, 1, 0);
}

AreaLight::AreaLight(unsigned int _id, const Vector3D &_pos, const Vector3D &_a, const Vector3D &_b, const Color &_c, double _e)
    : Light(_id, _c, _e), position(_pos), a(_a), b(_b){
}

AreaLight::~AreaLight(){
}

Color AreaLight::getIntersectionColor(const NearestInfo &_nearest, const Vector3D &_view, HBBTree *_tree, const unsigned int &_attenuation) const
{

    double xi1 = (rand()%100)/100.0;//numeros aleatorios entre [0, 1)
    double xi2 = (rand()%100)/100.0;//numeros aleatorios entre [0, 1)

    Vector3D r = position + (a*xi1) + (b*xi2);
    Vector3D toLight = r - _nearest.point;
    Vector3D toLightNormalized = toLight.normalize();

    if(isOccluded(_nearest, toLightNormalized, _tree, r)){
        return Color(0.0, 0.0, 0.0, 0.0);
    }

    double cosI = _nearest.normal.dotProduct(toLightNormalized);

    Vector3D reflected = (_nearest.normal * (2 * cosI)) - toLightNormalized;


    Material material = _nearest.object->getMaterial();
    Color materialDiffuse =  material.diffuse;
    Color materialSpecular =  material.specular;

    if(material.useTexture() && (material.texture->coloringType == REPLACE))
    {
        materialDiffuse = _nearest.texture;
        materialSpecular = _nearest.texture;
    }

    Color diffuse (0,0,0,0), specular(0,0,0,0);
    double diffuseDot = _nearest.normal.dotProduct( toLightNormalized );
    if(diffuseDot > 0)
        diffuse = materialDiffuse * ( material.kd * diffuseDot);

    double specularDot = reflected.normalize().dotProduct(_view);
    if(specularDot > 0)
        specular = materialSpecular * ( material.ks * ( pow( specularDot , material.specularPower) ) );

    Color result = color * (diffuse + specular);

    return result;// * attenuation(_attenuation, distance); //a cor vezes a atenuação;
}

void AreaLight::shine(const GLenum &num)
{
    GLfloat position__[3];
    GLfloat color__[4];
    glLightfv(num, GL_POSITION, (position+(a*0.5)+(b*0.5)).toGL(position__));
    glLightfv(num, GL_AMBIENT, color.toGL(color__));
    glLightfv(num, GL_DIFFUSE,  color__);
    glLightfv(num, GL_SPECULAR, color__);

    glEnable(num);
}

void AreaLight::draw() const
{
	glPushMatrix();


	glTranslated(position.x, position.y, position.z);

    glEnable(GL_COLOR_MATERIAL);
	glDisable(GL_LIGHTING);
	glDisable(GL_CULL_FACE);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);


    Vector3D p0 = Vector3D(0,0,0);
    Vector3D p1 = a;
    Vector3D p2 = a + b;
    Vector3D p3 = b;
    Vector3D n = (p0 - p1).crossProduct(p2 - p1).normalize();

    if(selected) glColor3d(1, 0, 0);
    else glColor4d(color.red, color.green, color.blue, color.alpha);
    glBegin(GL_QUADS);
        glNormal3d(-n.x, -n.y, -n.z);
        glVertex3d(p0.x, p0.y, p0.z);
        glVertex3d(p1.x, p1.y, p1.z);
        glVertex3d(p2.x, p2.y, p2.z);
        glVertex3d(p3.x, p3.y, p3.z);
    glEnd();

    glEnable(GL_CULL_FACE);
    glEnable(GL_LIGHTING);
    glDisable(GL_COLOR_MATERIAL);


	glPopMatrix();
}

//void AreaLight::calculateProjectionMap(HBBTree *_tree)
//{
//
//}

void AreaLight::generatePhotonPointDirection(Vector3D *_pos, Vector3D *_dir, Object *_object) const
{
    //a posição é a posição da luz
    double xi1 = (rand()%100)/100.0;//numeros aleatorios entre [0, 1)
    double xi2 = (rand()%100)/100.0;//numeros aleatorios entre [0, 1)
    *_pos = position + (a*xi1) + (b*xi2);


    if(_object)
    {
        *_dir = objectMap(*_pos, _object);
    }
    else
    {
        double ns = (rand()%101)/100.0;//[0,1]
        double us = (rand()%201)/100.0 - 1;//[-1,1]
        double vs = (rand()%201)/100.0 - 1;//[-1,1]

        Vector3D normal = a.crossProduct(b).normalize();

        *_dir = ((normal * ns) + (a.normalize()*us) + (b.normalize()*vs)).normalize();
    }

    //se fizer o mapa de projeção, essa direção irá depender dele
}
